#pragma once

#include "../mqcommon/log.hpp"
#include "../mqcommon/helper.hpp"

#include <iostream>
#include <sstream>
#include <mutex>
#include <memory>
#include <unordered_map>

#include <cassert>

namespace mymq
{
    struct MsgQueue
    {
        using ptr = std::shared_ptr<MsgQueue>;
        std::string name;
        bool durable;
        bool exclusive;
        bool auto_delete;
        google::protobuf::Map<std::string, std::string> args;
        MsgQueue() = default;
        MsgQueue(const std::string &qname, bool qdurable, bool qexclusive, bool qauto_delete,
                 const google::protobuf::Map<std::string, std::string> &qargs)
            : name(qname), durable(qdurable), exclusive(qexclusive), auto_delete(qauto_delete), args(qargs)
        {
        }
        // 相当于反序列化，把key=val&key=val...解析出来存储到args中
        void SetArgs(const std::string &str_args)
        {
            std::vector<std::string> sub_args;
            StrHelper::Split(str_args, "&", &sub_args);
            for (const auto &arg : sub_args)
            {
                size_t pos = arg.find('=');
                std::string key = arg.substr(0, pos);
                std::string value = arg.substr(pos + 1);
                args[key] = value;
            }
        }
        // 相当于序列化，把args中的数据解析为key=val&key=val...，用于持久化
        std::string GetArgs()
        {
            std::string result;
            for (const auto &arg : args)
            {
                result += arg.first + "=" + arg.second + "&";
            }
            return result;
        }
    };

    // 队列数据持久化类
    class MsgQueueMapper
    {
    public:
        MsgQueueMapper(const std::string &dbfile)
         : _sql_helper(dbfile)
        {
            if (!FileHelper(dbfile).Exists())
            {
                std::string ppath = FileHelper::ParentDirectory(dbfile);
                FileHelper::CreateDirectory(ppath);
            }
            assert(_sql_helper.Open());
            CreateTable();
        }
        void CreateTable()
        {
            std::stringstream ss;
            ss << "create table if not exists msgqueue_table(";
            ss << "name varchar(32) primary key,";
            ss << "durable int,";
            ss << "exclusive int,";
            ss << "auto_delete int,";
            ss << "args varchar(128));";
            bool ret = _sql_helper.Exec(ss.str().c_str(), nullptr, nullptr);
            if (ret == false)
            {
                FATAL_LOG("创建队列数据库表失败");
                abort();
            }
        }
        void RemoveTable()
        {
            bool ret = _sql_helper.Exec("drop table if exists msgqueue_table;", nullptr, nullptr);
            if (ret == false)
            {
                FATAL_LOG("删除队列数据库表失败");
                abort();
            }
        }
        void Insert(const MsgQueue::ptr &msgqueue)
        {
            std::stringstream ss;
            ss << "insert into msgqueue_table values(";
            ss << "'" << msgqueue->name << "', ";
            ss << msgqueue->durable << ", ";
            ss << msgqueue->exclusive << ", ";
            ss << msgqueue->auto_delete << ", ";
            ss << "'" << msgqueue->GetArgs() << "');";
            bool ret = _sql_helper.Exec(ss.str().c_str(), nullptr, nullptr);
            if (ret == false)
            {
                ERROR_LOG("插入队列数据库表数据失败");
            }
        }
        void Remove(const std::string &name)
        {
            std::stringstream ss;
            ss << "delete from msgqueue_table where name='";
            ss << name << "';";
            bool ret = _sql_helper.Exec(ss.str().c_str(), nullptr, nullptr);
            if (ret == false)
            {
                ERROR_LOG("删除队列数据库表数据失败");
            }
        }
        using MsgQueueMap = std::unordered_map<std::string, MsgQueue::ptr>;
        std::unordered_map<std::string, MsgQueue::ptr> Recovery()
        {
            MsgQueueMap result;
            bool ret = _sql_helper.Exec("select * from msgqueue_table;", SelectCallback, &result);
            if (ret == false)
            {
                ERROR_LOG("恢复队列数据库表数据失败");
            }
            return result;
        }

    private:
        static int SelectCallback(void *arg, int numcol, char **row, char **fields)
        {
            MsgQueueMap *result = (MsgQueueMap *)arg;
            MsgQueue::ptr mqp = std::make_shared<MsgQueue>();
            mqp->name = row[0];
            mqp->durable = (bool)std::stoi(row[1]);
            mqp->exclusive = (bool)std::stoi(row[2]);
            mqp->auto_delete = (bool)std::stoi(row[3]);
            if (row[4])
                mqp->SetArgs(row[4]);
            result->insert(std::make_pair(mqp->name, mqp));
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    class MsgQueueManager
    {
    public:
        using ptr = std::shared_ptr<MsgQueueManager>;
    public:
        MsgQueueManager(const std::string &dbfile)
            : _mapper(dbfile), _msgqueues(_mapper.Recovery())
        {
        }
        bool DeclareMsgQueue(const std::string &name, bool durable, bool exclusive, bool auto_delete,
                             const google::protobuf::Map<std::string, std::string> &args)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _msgqueues.find(name);
            if (it != _msgqueues.end())
            {
                INFO_LOG("声明队列失败！该队列%s已经存在，无须声明", name.c_str());
                return false;
            }
            MsgQueue::ptr mqp = std::make_shared<MsgQueue>(name, durable, exclusive, auto_delete, args);
            _msgqueues.insert(std::make_pair(name, mqp));
            if (durable == true) _mapper.Insert(mqp);
            return true;
        }
        bool DeleteMsgQueue(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _msgqueues.find(name);
            if (it == _msgqueues.end())
            {
                INFO_LOG("删除队列失败！该队列%s不存在，无须删除", name.c_str());
                return false;
            }
            _msgqueues.erase(name);
            if (it->second->durable == true) _mapper.Remove(name);
            return true;
        }
        MsgQueue::ptr SelectMsgQueue(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _msgqueues.find(name);
            if (it == _msgqueues.end())
                return MsgQueue::ptr();
            return it->second;
        }
        std::unordered_map<std::string, MsgQueue::ptr> GetAllMsgQueue()
        {
            std::unique_lock<std::mutex> lock(_mtx);
            return _msgqueues;
        }
        bool Exists(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _msgqueues.find(name);
            if (it == _msgqueues.end())
                return false;
            return true;
        }
        void Clear()
        {
            std::unique_lock<std::mutex> lock(_mtx);
            _msgqueues.clear();
            _mapper.RemoveTable();
        }
        size_t Size()
        {
            std::unique_lock<std::mutex> lock(_mtx);
            return _msgqueues.size();
        }
    private:
        std::mutex _mtx;
        MsgQueueMapper _mapper;
        std::unordered_map<std::string, MsgQueue::ptr> _msgqueues;
    };
}